/**
This file is part of javaee-patterns.

javaee-patterns is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

javaee-patterns is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.opensource.org/licenses/gpl-2.0.php>;.

 * Copyright (c) 22. June 2009 Adam Bien, blog.adam-bien.com
 * http://press.adam-bien.com
 */
package util.crudService;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import javax.persistence.Query;

/**
 * A minimalistic implementation of the generic CrudService.
 * @author adam-bien.com
 */
public class CrudService {

    private EntityManagerFactory emf = null;
    private EntityManager em = null;

    public CrudService() {
        emf = Persistence.createEntityManagerFactory("oraculoPU");
        em = emf.createEntityManager();
    }

    public EntityManager getEntityManager() {
        if (em == null || !em.isOpen()) {
            em = emf.createEntityManager();
        }
        return em;
    }

    public void create(List l) {
        try {
            getEntityManager().getTransaction().begin();
            for (Object o : l) {
                getEntityManager().persist(o);
            }
            getEntityManager().getTransaction().commit();
        } catch (Exception e) {
            getEntityManager().getTransaction().rollback();
            System.out.print(e);
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public <T> T create(T t) {
        try {
            getEntityManager().getTransaction().begin();
            getEntityManager().persist(t);
            getEntityManager().flush();
            getEntityManager().refresh(t);
            getEntityManager().getTransaction().commit();

        } catch (Exception e) {
            getEntityManager().getTransaction().rollback();
            System.out.print(e);
        } finally {
            if (em != null) {
                em.close();
            }
            return t;
        }
    }

    @SuppressWarnings("unchecked")
    public <T> T find(Class<T> type, Object id) {
        return (T) getEntityManager().find(type, id);
    }

    @SuppressWarnings("unchecked")
    public <T> List<T> findAll(Class<T> entityClass) {
        Query query = getEntityManager().createQuery("select e from "
                + entityClass.getSimpleName() + " e");
        return query.getResultList();
    }

    public List findWithCustomQuery(String query, Map args) {
        Query q = getEntityManager().createQuery(query);
        for (Object key : args.keySet()) {
            q.setParameter(key.toString(), args.get(key));
        }
        return q.getResultList();
    }

    public <T> List<T> findAllOrderBy(Class<T> entityClass, String colAttrName) {
        Query query = getEntityManager().createQuery("select e from "
                + entityClass.getSimpleName() + " e order by e." + colAttrName);
        return query.getResultList();
    }

    public void delete(Class type, Object id) {
        Object ref = getEntityManager().getReference(type, id);
        getEntityManager().remove(ref);
    }

    public <T> T update(T t) {
        return (T) getEntityManager().merge(t);
    }

    public List findWithNamedQuery(String namedQueryName) {
        return getEntityManager().createNamedQuery(namedQueryName).getResultList();
    }

    public List findWithNamedQuery(String namedQueryName, Map<String, Object> parameters) {
        return findWithNamedQuery(namedQueryName, parameters, 0);
    }

    public List findWithNamedQuery(String queryName, int resultLimit) {
        return getEntityManager().createNamedQuery(queryName).
                setMaxResults(resultLimit).
                getResultList();
    }

    public List findByNativeQuery(String sql, Class type) {
        return getEntityManager().createNativeQuery(sql, type).getResultList();
    }

    public void executeNativeQuery(String sql) {
        getEntityManager().createNativeQuery(sql);
    }

    public <T> T findSingleWithNamedQuery(String namedQueryName, Map<String, Object> parameters) {
        Set<Entry<String, Object>> rawParameters = parameters.entrySet();
        Query query = getEntityManager().createNamedQuery(namedQueryName);

        for (Entry<String, Object> entry : rawParameters) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
        try {
            return (T) query.getSingleResult();
        } catch (javax.persistence.NoResultException e) {
            return null;
        }
    }

    public List findWithNamedQuery(String namedQueryName, Map<String, Object> parameters, int resultLimit) {
        Set<Entry<String, Object>> rawParameters = parameters.entrySet();
        Query query = getEntityManager().createNamedQuery(namedQueryName);
        if (resultLimit > 0) {
            query.setMaxResults(resultLimit);
        }
        for (Entry<String, Object> entry : rawParameters) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
        return query.getResultList();
    }

    public int executeQuery(String queryString, Map<String, Object> parameters) {
//      Exemplo de query
//      Query query = em.createQuery("DELETE USER u WHERE u.status = :status ");
//      query.setParameter ("status",'GOLD');

        Set<Entry<String, Object>> rawParameters = parameters.entrySet();

        Query query = getEntityManager().createQuery(queryString);
        for (Entry<String, Object> entry : rawParameters) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
        return query.executeUpdate();
    }
}
